/******
version：batteryPiV6.1
一、基本功能：低功耗功能
二、本版本新增功能：增加闹钟模式，分为节能模式和无睡眠模式
三、本版本新增EEPROM功能，存入闹钟模式，闹钟起始时间，闹钟结束时间，闹钟周期，闹钟周期间隔实现如下功能：
1、闹钟模式，闹钟起始时间，闹钟结束时间，闹钟周期，闹钟周期间隔可调。
2、闹钟模式，闹钟起始时间，闹钟结束时间，闹钟周期，闹钟周期间隔存入EEPROM，断电不丢失

最终版本
******/
/*设置avr-gcc的优化级别，arduino默认优化级别是-Os
-O0 No optimization. This is the same as not specifying any optimization. 
-O1 Optimize. Reduces code size and execution time without performing any optimizations that take a great deal of compilation time. 
-O2 Optimize even more. avr-gcc performs almost all optimizations that don't involve a space-time tradeoff. 
-O3 Optimize yet more. This level performs all optimizations at -O2 along with -finline-functions and - frename-registers. 
-Os Optimize for size. Enables all -O2 optimizations that don't increase code size. It also performs further optimizations designed to reduce code size.
*/
#pragma GCC optimize ("-Os")

#include "PowerManagementHAT.h"
#include "Time.h"
#include "LowPower.h"
#include "PCF8523.h"
#include <Wire.h>
#include <avr/wdt.h>  
#include <avr/sleep.h>
#include <EEPROM.h>
//debug开关
//#define DEBUG
//模拟休眠开关
//#define SIM_SW

//全局变量
//读取时间用的tmElements_t结构体
//用户led，就是led灯的引脚
#define ledpin 13
//外部中断引脚
#define intpin 2

//串口命令
String cmdstr="";//命令字符串
String cmdtype="";//命令类型
String cmdcontent="";//命令内容


/*****************
周期的一些基础配置
*****************/
/********动作结构****************
闹钟周期模式，分为2种：
1、低功耗模式：按照周期进行睡眠，周期开始，arduino唤醒指定的时长(时长小于周期)；唤醒时间过后，arduino进入低功耗状态(sleep_cpu)。
2、无睡眠模式：全程没有睡眠，等待树莓派发出命令。
********************************/

//记录当前动作的全局变量
bool loopOn=false;
bool piOn=false;

//周期模式
#define LOWPOWERMODE 0x01
#define NOSLEEP      0x02
//动作起止时间配置
#define STARTHOUR 7
#define STARTMINUTE 30
#define ENDHOUR 12
#define ENDMINUTE 5
//周期唤醒的循环周期(单位：分钟)
#define ALARMCYCLE 10
//周期唤醒的唤醒时间长度(单位：分钟)
#define ALARMCYCLELONG 5

//记录闹钟信息的结构体

typedef struct{
    uint8_t alarmmode;//记录闹钟模式,默认低功耗模式
    uint8_t starthour;//闹钟起始时间小时数
    uint8_t startminute;//闹钟结束时间分钟数
    uint8_t endhour;//闹钟结束时间小时数
    uint8_t endminute;//闹钟结束分钟小时数
    uint8_t alarmcycle;//闹钟循环周期(分钟)
    uint8_t alarmcyclelong;//闹钟循环持续时间(分钟)
}AlarmInfo;

//全局变量，用于记录闹钟信息
AlarmInfo alarminfo;

//中断引脚数据
uint8_t volatile intpinval=0;
//记录是否需要重新生成闹钟的全局变量
bool volatile newAlarm = false;

//唤醒时，打印时间用的全局变量
#ifdef SIM_SW
int loopcnt=0;
#endif


void setup() {
  //设置LED灯端口输出
    pinMode(ledpin, OUTPUT);
    //关闭LED节能
    digitalWrite(ledpin,HIGH);
    //设置中断口为输入
    pinMode(intpin, INPUT);
    //串口初始化
    Serial.begin(9600);
    //启动printf
    printf_begin();
    Serial.println("System is woking now");
    delay(500);
    
    
    /*RTC初始化*/
    //初始化RTC，但是不会重置时间
    Serial.println("Init RTC");
    power.RTCInit(true);
    printNowTime();
    
    //alrminfo初始化
    readAlarmInfo();
    
    /*树莓派初始化*/
    //设置启动不打开树莓派
    power.connectPiPower(false);
    
    /*休眠设置*/
    //设置休眠模式为最省电的掉电模式
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    
    
    /*首次初始化闹钟*/
    Serial.println("Init Alarm");
    //设置闹钟
    setNextAlarm();

    /*开启中断*/
    //设置中断
    Serial.println("start INT0");
    attachInterrupt(digitalPinToInterrupt(intpin), alarm_Int, FALLING);
   
}

void loop() {
    if(newAlarm){
        //清除闹钟中断
        power.ackAlarm();
        #ifdef SIM_SW
        printNowTime();
        #endif
        setNextAlarm();
        newAlarm=false;
    }
    if(piOn){
        startPi();
    }else{
        stopPi();
    }
    //如果允许主循环
    if(loopOn){
        #ifdef SIM_SW
        if(loopcnt==100){
            Serial.print("awake......  ");
            printNowTime();
            loopcnt=0;
        }
        loopcnt++;
        #endif
        readCmd();
        delay(10);
    }
    //模拟休眠
    #ifdef SIM_SW
    while(!loopOn&&!newAlarm){
        if(loopcnt==100){
            Serial.print("sleeping......  ");
            printNowTime();
            loopcnt=0;
        }
        readCmd();
        loopcnt++;
        delay(10);
    }
    #endif
    
    #ifndef SIM_SW
    if(!loopOn){
        //休眠
        Serial.println("============= enter sleep =============");
        Serial.flush();
        sleep_cpu();
    }
    #endif
    
}

//闹钟中断函数
void alarm_Int(void)
{
    intpinval = digitalRead(intpin);
    if(!intpinval){
        newAlarm=true;
    }
}
/***************重点业务逻辑********************/
//设置下一次闹钟时间
void setNextAlarm(void)
{
    DateTime now = power.readTime();    
    
    DateTime startDt=DateTime(now.year(),now.month(),now.day(),alarminfo.starthour,alarminfo.startminute,0);
    DateTime endDt=DateTime(now.year(),now.month(),now.day(),alarminfo.endhour,alarminfo.endminute,0);
    DateTime nextAlarm=DateTime(now);
    //如果是低功耗模式
    if(alarminfo.alarmmode==LOWPOWERMODE){
        if(now.secondstime()>=startDt.secondstime() && now.secondstime()<endDt.secondstime()){
            //在此期间，树莓派一定必须是开着的
            piOn=true;
            if(now.minute()%alarminfo.alarmcycle==0){
                //在此期间，主循环也是打开的
                loopOn=true;
                nextAlarm = now+TimeSpan(0,0,alarminfo.alarmcyclelong,0);
            }else{
                loopOn=false;
                do{
                    nextAlarm = nextAlarm+TimeSpan(0,0,1,0);
                }while(0!=nextAlarm.minute()%alarminfo.alarmcycle && nextAlarm.secondstime()<endDt.secondstime());
            }
            power.setAlarm(nextAlarm.hour(),nextAlarm.minute());
        }else{
            //此时一定设置主循环关闭，pi也关闭
            piOn=false;
            loopOn=false;
            //设置闹钟到7:30
            nextAlarm = startDt;
            power.setAlarm(nextAlarm.hour(),nextAlarm.minute());
        }
    }
    //如果是无睡眠模式
    if(alarminfo.alarmmode==NOSLEEP){
        if(now.secondstime()>=startDt.secondstime() && now.secondstime()<endDt.secondstime()){
            //在此期间，树莓派一定必须是开着的，主循环也开着
            piOn=true;
            loopOn=true;
            //直接设置闹钟到结束时间
            power.setAlarm(endDt.hour(),endDt.minute());
        }else{
            //此时一定设置主循环关闭，pi也关闭
            piOn=false;
            loopOn=false;
            //设置闹钟到7:30
            nextAlarm = startDt;
            power.setAlarm(nextAlarm.hour(),nextAlarm.minute());
        }
    }
    power.enableAlarm(true);
    printAlarm();
}


/************************************
串口printf基础工具函数
 ************************************/
/*
串口格式化输出
 */
int serial_putc( char c, struct __file * ) {
    Serial.write( c );
    return c;
}
void printf_begin(void) {
    fdevopen( &serial_putc, 0 );
}


/********************
工具函数
*******************/
//解析时间
void parseTime(uint8_t *hourptr,uint8_t *minuteptr){
    //举例解析: 15:14
    String tmpstr="";
    tmpstr = cmdcontent.substring(0, 2);
    *hourptr=tmpstr.toInt();
    
    tmpstr="";
    tmpstr = cmdcontent.substring(3,5);
    *minuteptr=tmpstr.toInt();

    #ifdef DEBUG
    Serial.print("parseval:");
    printf("%02d:%02d\n",*hourptr,*minuteptr);
    #endif
};

/*****************************************
自定义的函数
******************************************/
//读取串口命令
void readCmd(void) {
    while(Serial.available() > 0)//串口接收到数据
    {
        int inChar = Serial.read();
        if (inChar == '\n')
        {
            cmdstr+=(char)inChar;
            int idx = cmdstr.indexOf(' ');
            cmdtype="";
            cmdcontent="";
            if (-1==idx) {
                cmdstr.trim();
                cmdtype=cmdstr;
                #ifdef DEBUG
                Serial.println(cmdtype);
                #endif
            } else {
                cmdtype=cmdstr.substring(0,idx);
                #ifdef DEBUG
                Serial.println(cmdtype);
                #endif
                cmdcontent=cmdstr.substring(idx+1,cmdstr.length());
                #ifdef DEBUG
                Serial.println(cmdcontent);
                #endif
            }
            exeCmd();
            cmdstr="";
        }else{
            cmdstr+=(char)inChar;
        }
    }
}
/********************
重置命令
 *******************/
//执行命令
void exeCmd(void){
    /*空命令,啥也不做*/
    if(cmdtype.equals("")){
        Serial.println("no cmdtype");
    }
    /*
    基础测试
    */
    if (cmdtype.equals("test")) {
        Serial.println("test");
    }
    /****************AlarmInfo设置********************/
    //读取EEPROM数据
    if (cmdtype.equals("readAlarmInfo")) {
        readAlarmInfo();
    }
    //设置为不睡眠
    if (cmdtype.equals("setalarmmode_nosleep")) {
        alarminfo.alarmmode=NOSLEEP;
        writeAlarmInfo();//写EEPROM信息，要写都写
        setNextAlarm();
        Serial.println("setalarmmode_nosleep:OK");
    }
    //设置低功耗模式
    if (cmdtype.equals("setalarmmode_lowpower")) {
        alarminfo.alarmmode=LOWPOWERMODE;
        writeAlarmInfo();//写EEPROM信息，要写都写
        setNextAlarm();
        Serial.println("setalarmmode_lowpower:OK");
    }
    //读取睡眠模式
    if (cmdtype.equals("getalarmmode")) {
        Serial.print("getalarmmode:");
        if(alarminfo.alarmmode==LOWPOWERMODE){
            Serial.println("LOWPOWERMODE");
        }else{
            Serial.println("NOSLEEP");
        }
    }
    
    //设置闹钟起始时间
    if (cmdtype.equals("setStartTime")) {
        if (cmdcontent.length()==6){
            uint8_t shour;
            uint8_t sminute;
            parseTime(&shour,&sminute);
            
            #ifdef DEBUG
            Serial.print("shour:");
            Serial.println(shour);
            Serial.print("sminute:");
            Serial.println(sminute);
            #endif    
            setStartTime(shour,sminute);
            Serial.println("setStartTime:OK");
        }else{
            Serial.println("setStartTime:error");
        }
    }
    //设置闹钟结束时间
    if (cmdtype.equals("setEndTime")) {
        if (cmdcontent.length()==6){
            uint8_t ehour;
            uint8_t eminute;
            parseTime(&ehour,&eminute);
            
            #ifdef DEBUG
            Serial.print("ehour:");
            Serial.println(ehour);
            Serial.print("eminute:");
            Serial.println(eminute);
            #endif    
            setEndTime(ehour,eminute);
            Serial.println("setEndTime:OK");
        }else{
            Serial.println("setEndTimeL:error");
        }
    }
    
    /****************树莓派控制********************/
    //普通开启树莓派
    if (cmdtype.equals("setPiOn")) {
        startPi();
        Serial.println("setPiOn:OK");
    }
    //关闭树莓派
    if (cmdtype.equals("shutPiDown")) {
        stopPi();
        Serial.println("shutPiDown:OK");
    }
    //普通获取树莓派状态
    if (cmdtype.equals("getPiStatus")) {
        bool status = power.getPiStatus(false);
        Serial.print("getPiStatus:");
        Serial.println(status);
    }

    /***********电压电流操作***************/
    //读取电压
    if (cmdtype.equals("readVolt")) {
        float vol = power.measureVoltage();
        Serial.print("readVolt:");
        Serial.println(vol);
    }
    //读取电流
    if (cmdtype.equals("readCurrent")) {
        float current = power.measureCurrent();
        Serial.print("readCurrent:");
        Serial.println(current);
    }
  //读取电流和电流
    if (cmdtype.equals("readVoltCurrent")) {
        float vol = power.measureVoltage();
        float current = power.measureCurrent();
        Serial.print("volt:");
        Serial.print(vol);
        Serial.print(" current:");
        Serial.println(current);
    }
    /****************时钟操作***************/
    if (cmdtype.equals("setTime")) {
        if(cmdcontent.length()==20) {
            mySetTime(true);
            Serial.println("setTime:OK");
        }else{
            Serial.println("setTime:error");
        }
    }
    if (cmdtype.equals("setTimeNoAlarm")) {
        if(cmdcontent.length()==20) {
            mySetTime(false);
            Serial.println("setTimeNoAlarm:OK");
        }else{
            Serial.println("setTimeNoAlarm:error");
        }
    }
    //读取时间
    if (cmdtype.equals("readTime")) {
        Serial.print("readTime:");
        printNowTime();
    }
    //读取闹钟
    if (cmdtype.equals("readAlarm")) {
        Serial.print("readAlarm:");
        printAlarm();        
    }
    //设置不包含天数的闹钟
    if (cmdtype.equals("setAlarm")) {
        if (cmdcontent.length()==6){
            uint8_t hour;
            uint8_t minute;
            parseTime(&hour,&minute);
            
            #ifdef DEBUG
            printf("set alarm %02d:%02d\n",hour,minute);
            #endif
            power.setAlarm(hour,minute);
            Serial.print("setAlarm:");
            printAlarm();
            
        }else{
            Serial.println("setAlarm:error");
        }
    }
    //允许闹钟中断
    if (cmdtype.equals("enableAlarm")) {
        if (cmdcontent.length()==0){
            Serial.println("enableAlarm:error");
        }else{
            cmdcontent.trim();
            if(cmdcontent.equals("true")){
                power.enableAlarm(true);
            }else{
                power.enableAlarm(false);
            }
            Serial.println("enableAlarm:OK");
        }
        
    }
}

/*************************
AlarmInfo以及EEPROM控制封装函数
**************************/

//设置闹钟起始时间
void setStartTime(uint8_t shour,uint8_t sminute){
    unsigned int newstarttime=shour*60+sminute;
    unsigned int endtime =alarminfo.endhour*60+alarminfo.endminute;
    
    #ifdef DEBUG
    Serial.print("newstarttime:");
    Serial.println(newstarttime);
    Serial.print("endtime:");
    Serial.println(endtime);
    #endif
    
    if(newstarttime>=endtime){
        printf("newstarttime:%02d:%02d must befor endtime:%02d:%02d\n",shour,sminute,alarminfo.endhour,alarminfo.endminute);
    }else{
        alarminfo.starthour=shour;
        alarminfo.startminute=sminute;
        writeAlarmInfo();//写EEPROM信息，要写都写
    }
    setNextAlarm();
}

//设置闹钟终止时间
void setEndTime(uint8_t ehour,uint8_t eminute){
    unsigned int newendtime=ehour*60+eminute;
    unsigned int starttime = alarminfo.starthour*60+alarminfo.startminute;
    #ifdef DEBUG
    Serial.print("newendtime:");
    Serial.println(newendtime);
    Serial.print("starttime:");
    Serial.println(starttime);
    #endif
    if(newendtime<=starttime){
        printf("newendtime:%02d:%02d must after starttime:%02d:%02d\n",ehour,eminute,alarminfo.starthour,alarminfo.startminute);
    }else{
        alarminfo.endhour=ehour;
        alarminfo.endminute=eminute;
        writeAlarmInfo();//写EEPROM信息，要写都写
    }
    setNextAlarm();
}


//打印读取AlarmInfo
void printAlarmInfo(){
    if(alarminfo.alarmmode==LOWPOWERMODE){
        Serial.print("alarmmode:LOWPOWERMODE");
    }else{
        Serial.print("alarmmode:NOSLEEP");
    }
    Serial.print(" starthour:");
    Serial.print(alarminfo.starthour);
    Serial.print(" startminute:");
    Serial.print(alarminfo.startminute);
    Serial.print(" endhour:");
    Serial.print(alarminfo.endhour);
    Serial.print(" endminute:");
    Serial.print(alarminfo.endminute);
    Serial.print(" alarmcycle:");
    Serial.print(alarminfo.alarmcycle);
    Serial.print(" alarmcyclelong:");
    Serial.println(alarminfo.alarmcyclelong);
}

//读取AlarmInfo
void readAlarmInfo(void){
    alarminfo.alarmmode=EEPROM.read(0);
    alarminfo.starthour=EEPROM.read(1);
    alarminfo.startminute=EEPROM.read(2);
    alarminfo.endhour=EEPROM.read(3);
    alarminfo.endminute=EEPROM.read(4);
    alarminfo.alarmcycle=EEPROM.read(5);
    alarminfo.alarmcyclelong=EEPROM.read(6);
    printAlarmInfo();
}
//写AlarmInfo
void writeAlarmInfo(void){
    EEPROM.write(0,alarminfo.alarmmode);
    EEPROM.write(1,alarminfo.starthour);
    EEPROM.write(2,alarminfo.startminute);
    EEPROM.write(3,alarminfo.endhour);
    EEPROM.write(4,alarminfo.endminute);
    EEPROM.write(5,alarminfo.alarmcycle);
    EEPROM.write(6,alarminfo.alarmcyclelong);
    
    printAlarmInfo();
}
/*************************
树莓派控制封装函数
**************************/
void startPi(void){
    int  handShake=0;
    unsigned long timeStart, timeNow, testTime;
    bool isOn = power.getPiStatus(false);
    if(isOn){
        //Serial.println("Pi is running...");
        return;
    }
    Serial.println("starting...");
    Serial.flush();
    
    power.connectPiPower(true);
    timeStart = millis();
    testTime = 0;
    handShake = digitalRead(7);
    while((handShake == 0) && (testTime < MAX_SHUT_DOWN_TIME)) {
        handShake = digitalRead(7);
        delay(50);
        timeNow = millis();
        testTime = timeNow - timeStart;
    }
    
    bool status = power.getPiStatus(false);
    Serial.print("pistatus:");
    Serial.println(status);
}
void stopPi(void){
    bool isOn = power.getPiStatus(false);
    if(!isOn){
        //Serial.println("Pi is not running...");
        return;
    }
    Serial.println("shutPiDown...");
    power.shutPiDown();
    Serial.print("pistatus:");
    bool status = power.getPiStatus(false);
    Serial.println(status);
}

/***************************
时间设置类，复杂自定义函数
这里记得修改一个pcf8523.cpp里的bug
第288行，把
//WIRE._I2C_WRITE(0);
这里注释掉，库里写错了
 **************************/
void mySetTime(bool withAlarm)
{
    uint16_t year;
    uint8_t month;
    uint8_t day;
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    
    
     //举例解析:2020-12-19 15:14:00
    String tmpstr="";
    tmpstr=cmdcontent.substring(0,4);
    year=tmpstr.toInt();
    
    tmpstr="";
    tmpstr=cmdcontent.substring(5,7);
    month=tmpstr.toInt();

    tmpstr="";
    tmpstr=cmdcontent.substring(8,10);
    day=tmpstr.toInt();
    
    tmpstr="";
    tmpstr=cmdcontent.substring(11,13);
    hour=tmpstr.toInt();
    
    tmpstr="";
    tmpstr=cmdcontent.substring(14,16);
    minute=tmpstr.toInt();
    
    tmpstr="";
    tmpstr=cmdcontent.substring(17,19);
    second=tmpstr.toInt();
    
    #ifdef DEBUG
    printAlarm();
    #endif
    power.setTime(DateTime(year,month,day,hour,minute,second));
    printNowTime();
    if(withAlarm){
        Serial.println("set Alarm");
        setNextAlarm();
    }
    #ifdef DEBUG
    printAlarm();
    #endif
}

//打印闹钟
void printAlarm(void){
    ALARM_SETTINGS tmp;
    power.getAlarm(&tmp);
    printf("alarm %02d:%02d %d:%d ",tmp.hours,tmp.minutes,tmp.hoursEnabled,tmp.minutesEnabled);
    
    if(tmp.twentyFourHour==eTWENTYFOURHOUR){
        Serial.print(" e12_24:");
        Serial.println("24h");
    }
    if(tmp.twentyFourHour==eTWELVEHOUR){
        Serial.print(" e12_24:");
        Serial.print("12h");
        if(tmp.AmPm==eAM){
            Serial.print(" eAM_PM:");
            Serial.println("AM");
        }
        if(tmp.AmPm==ePM){
            Serial.print(" eAM_PM:");
            Serial.println("PM");
        }
    }
    
}
//打印时间
void printNowTime(void) {
    // Read the time
    DateTime now = power.readTime();
    printf("%04d-%02d-%02d %02d:%02d:%02d\n",now.year(),now.month(),now.day(),now.hour(),now.minute(),now.second());
}
//打印DateTime类
void printDateTime(DateTime dt){
    printf("%04d-%02d-%02d %02d:%02d:%02d\n",dt.year(),dt.month(),dt.day(),dt.hour(),dt.minute(),dt.second());
}
//打印Action
void printAction(void){
    Serial.print("loopOn:");
    Serial.print(loopOn);
    Serial.print(" piOn:");
    Serial.println(piOn);
}
